/*
 * Copyright 2009 Eat Local Food, LLC
 * Copyright (c) 2007 osCommerce (this file was written after
 * code review of osCommerce).
 *
 * This file is part of gwtCommerce.
 *
 * gwtCommerce is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * gwtCommerce is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gwtCommerce.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.elf.gwt.store.client.components;

import java.util.Iterator;
import java.util.List;

import com.elf.gwt.common.client.GWTCommerceCommonConstants;
import com.elf.gwt.common.client.access.RequestParameter;
import com.elf.gwt.common.client.callback.SubComponentCallback;
import com.elf.gwt.common.client.components.CommonSimpleComponent;
import com.elf.gwt.common.client.model.AccountMetaModel;
import com.elf.gwt.common.client.model.AccountModel;
import com.elf.gwt.common.client.model.AddressModel;
import com.elf.gwt.common.client.model.AttributeValueModel;
import com.elf.gwt.common.client.model.CartModel;
import com.elf.gwt.common.client.model.CheckoutMetaModel;
import com.elf.gwt.common.client.model.CheckoutModel;
import com.elf.gwt.common.client.model.CountryModel;
import com.elf.gwt.common.client.model.DetailedProductModel;
import com.elf.gwt.common.client.model.OrderTotalModel;
import com.elf.gwt.common.client.subcomponent.CommonSubComponent;
import com.elf.gwt.common.client.utils.CommonUtils;
import com.elf.gwt.store.client.GWTCommerceConstants;
import com.elf.gwt.store.client.StoreComponentController;
import com.elf.gwt.store.client.subcomponents.CheckoutAddressSubComponent;
import com.elf.gwt.store.client.subcomponents.CheckoutPaymentSubComponent;
import com.elf.gwt.store.client.subcomponents.CheckoutShippingSubComponent;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;

/**
 *  
 */
public class CheckoutComponent extends JSONServerComponent
{	
	protected CheckoutMetaModel checkoutMetaModel;
	protected CheckoutModel checkoutModel;

	public static final String Checkout = "Checkout";
	public static final String ProcessPayment = "ProcessPayment";

	protected List<CountryModel> countryModels;

	protected FlowPanel bodyPanel;
	protected InlineLabel checkoutInstructionsLabel;
	protected InlineLabel checkoutThankyouLabel;

	protected InlineLabel requiredTitle;
	protected CheckoutAddressSubComponent shipToAddressSubComponent;
	protected CheckoutAddressSubComponent billToAddressSubComponent;
	protected CheckBox sameAsShipTo;
	protected InlineHTML sameAsShipToLabel;

	protected CheckoutShippingSubComponent shippingSubComponent;

	protected CheckoutPaymentSubComponent paymentSubComponent;

	protected InlineLabel couponTitle;
	protected TextBox couponTextBox;
	protected String couponText = "";
	protected Grid couponGrid;

	protected InlineLabel commentTitle;
	protected TextArea commentTextArea;
	protected InlineLabel commentText;

	protected VerticalPanel totalsPanel;

	protected AccountMetaModel accountMetaModel;
	protected Button paymentButton;
	protected InlineHTML warningLabel;
	protected String action = "";
	protected String tempSelectedPaymentModule = "";

	/**
	 * Constructor
	 */
	public CheckoutComponent(StoreComponentController controller, String stylePrefix)
	{
		super(controller, stylePrefix, null, false);
		checkoutModel = new CheckoutModel();
		setupTimer();
		bodyPanel = null;
		shippingSubComponent = null;
		this.setLoginRequired(true);
	}

	public boolean updateFromHistoryParameters(String[] params)
	{
		if (storeController.thirdPartyPaymentProcess) 
		{
			storeController.thirdPartyPaymentProcess = false;
			controller.hideComponents();
			this.setVisible(true);
			return false;
		}

		if (!super.updateFromHistoryParameters(params)) return false;

		storeController.getMultipleResultsComponent().addComponent(this);

		//Don't look for new products
		CommonSimpleComponent alwaysAskComponent = controller.getComponent("NewProductsComponent", false);
		if (alwaysAskComponent != null) storeController.getMultipleResultsComponent().removeComponent(alwaysAskComponent);

		action = "";

		if (checkoutModel != null) {
			checkoutModel.setSelectedShippingModule("");
			checkoutModel.setSelectedShippingService("");
		}

		return true;
	}

	public void additionalRequestParameters()
	{
		GWT.log("CheckoutComponent.additionalRequestParameters start");
		AccountModel accountModel = StoreComponentController.accountComponent.getAccountModel();
		accountMetaModel = accountModel.getAccountMetaModel();
		addRequestParameter(new RequestParameter("action", action));


		GWT.log("CheckoutComponent.additionalRequestParameters output cart");
		CartModel cmodel = StoreComponentController.cartComponent.getCartModel();
		addRequestParameter(new RequestParameter("cart", cmodel.getCartContents()));

		GWT.log("CheckoutComponent.additionalRequestParameters output shipTo " + shipToAddressSubComponent);
		if (shipToAddressSubComponent == null)
		{
			AddressModel shipToAddressModel = accountModel.getDefaultAddress();
			addRequestParameter(new RequestParameter("shipToAddressId", shipToAddressModel.getId()));
		}
		else
		{
			List<RequestParameter> requestParameters = shipToAddressSubComponent.getAdditionalRequestParameters();
			for (Iterator<RequestParameter> iter = requestParameters.iterator(); iter.hasNext();)
			{
				RequestParameter rp = iter.next();
				rp.setKey("shipTo" + rp.getKey());
				addRequestParameter(rp);
			}
		}

		GWT.log("CheckoutComponent.additionalRequestParameters output billTo");
		if (billToAddressSubComponent == null)
		{
			AddressModel billToAddressModel = accountModel.getDefaultAddress();
			addRequestParameter(new RequestParameter("billToAddressId", billToAddressModel.getId()));
		}
		else
		{
			List<RequestParameter> requestParameters;
			if (sameAsShipTo.getValue())
				requestParameters = shipToAddressSubComponent.getAdditionalRequestParameters();
			else
				requestParameters = billToAddressSubComponent.getAdditionalRequestParameters();

			for (Iterator<RequestParameter> iter = requestParameters.iterator(); iter.hasNext();)
			{
				RequestParameter rp = iter.next();
				rp.setKey("billTo" + rp.getKey());
				addRequestParameter(rp);
			}
		}

		GWT.log("CheckoutComponent.additionalRequestParameters output shipping");
		addRequestParameter(new RequestParameter("shipping_module", checkoutModel.getSelectedShippingModule()));
		addRequestParameter(new RequestParameter("shipping_service", checkoutModel.getSelectedShippingService()));


		GWT.log("CheckoutComponent.additionalRequestParameters output coupon");
		if (couponText != null && couponText.length() > 0)
			addRequestParameter(new RequestParameter("coupon_code", couponText));

		GWT.log("CheckoutComponent.additionalRequestParameters output action" + action);
		if (action.equals(ProcessPayment))
		{

			addRequestParameter(new RequestParameter("payment_module", checkoutModel.getSelectedPaymentModule()));

			if (paymentSubComponent != null)
			{
				List<RequestParameter> requestParameters = paymentSubComponent.getAdditionalRequestParameters();
				for (Iterator<RequestParameter> iter = requestParameters.iterator(); iter.hasNext();)
				{
					RequestParameter rp = iter.next();
					addRequestParameter(rp);
				}
			}

			if (commentTextArea != null)
				addRequestParameter(new RequestParameter("checkoutComment", commentTextArea.getText()));
		}
		else
		{
			tempSelectedPaymentModule = checkoutModel.getSelectedPaymentModule();
		}
		GWT.log("CheckoutComponent.additionalRequestParameters end");

	}

	public void build(Throwable exception)
	{
		if (exception.getMessage() != null && exception.getMessage().equals("Error on Server: null")) {
			onSelection(null);
		}
		else {
			super.build(exception);
		}
	}

	/**
	 * Build by accessing the JSON Server for information.  This is the typical
	 * method used to create display.
	 */
	public void build()
	{
		this.build(false);
	}

	protected void buildMetaModel(JSONObject jsonObject)
	{
		checkoutMetaModel = new CheckoutMetaModel(jsonObject);
	}

	/**
	 * BuildData from JSONObject
	 * @param jsonObject
	 */
	protected void buildModel(JSONObject jsonObject) throws Exception
	{
		checkoutModel.update(jsonObject);
		if (tempSelectedPaymentModule != null && tempSelectedPaymentModule.length() > 0)
		{
			checkoutModel.setSelectedPaymentModule(tempSelectedPaymentModule);
			tempSelectedPaymentModule = "";
		}
	}

	/**
	 * Create the visuals for the component
	 */
	public void buildFromModel() throws Exception
	{
		GWTCommerceConstants constants = storeController.getStoreConstants();
		AccountModel accountModel = StoreComponentController.accountComponent.getAccountModel();
		AddressModel addressModel = accountModel.getDefaultAddress();
		accountMetaModel = accountModel.getAccountMetaModel();

		boolean checkoutCompleted = false;
		if (checkoutModel.getActionCompleted() != null && checkoutModel.getActionCompleted().equals(ProcessPayment))
		{
			checkoutModel.setActionCompleted("");
			action = "";
			checkoutCompleted = true;
		}
		if (accountMetaModel != null)
		{
			if (StoreComponentController.cartComponent.getCartModel().getProducts().size() == 0)
			{
				Window.scrollTo(0, 0);
				InlineLabel emptyCartLabel = new InlineLabel(constants.CheckoutComponentEmptyCart());
				emptyCartLabel.addStyleName(getHandle() + "BodyPanel");
				panel.setWidget(1, 0, emptyCartLabel);
			}
			else
			{
				countryModels = accountMetaModel.getCountryModels();

				if (bodyPanel == null)
				{
					Window.scrollTo(0, 0);
					bodyPanel = new FlowPanel();
					bodyPanel.setWidth("100%");
					bodyPanel.addStyleName(getHandle() + "BodyPanel");

					checkoutInstructionsLabel = new InlineLabel(constants.CheckoutComponentInstructions());
					bodyPanel.add(checkoutInstructionsLabel);
					checkoutInstructionsLabel.setVisible(!checkoutCompleted);
					checkoutThankyouLabel = new InlineLabel(constants.CheckoutComponentThankyou());
					bodyPanel.add(checkoutThankyouLabel);
					checkoutThankyouLabel.setVisible(checkoutCompleted);

					warningLabel = new InlineHTML();
					bodyPanel.add(warningLabel);

					bodyPanel.add(new InlineHTML("<br/>"));
					InlineLabel shipToTitle = new InlineLabel(constants.CheckoutComponentShipToAddress());
					shipToTitle.setStyleName(handle + "SubTitle");
					shipToTitle.setWordWrap(false);
					requiredTitle = new InlineLabel(constants.AccountComponentRequired());
					requiredTitle.setVisible(!checkoutCompleted);
					requiredTitle.setStyleName(handle + "RequiredTitle");
					HorizontalPanel shipToTitlePanel = new HorizontalPanel();
					shipToTitlePanel.setStyleName(handle + "ShipToTitlePanel");
					shipToTitlePanel.add(shipToTitle);
					shipToTitlePanel.add(requiredTitle);
					shipToTitlePanel.setCellWidth(requiredTitle, "100%");
					bodyPanel.add(shipToTitlePanel);
					shipToTitlePanel.setCellHorizontalAlignment(requiredTitle, HasHorizontalAlignment.ALIGN_RIGHT);

					shipToAddressSubComponent =
						new CheckoutAddressSubComponent(
								storeController, 
								addressSubComponentCallback);

					shipToAddressSubComponent.setAddressModel(addressModel);

					shipToAddressSubComponent.setCheckoutCompleted(checkoutCompleted);
					shipToAddressSubComponent.build();
					shipToAddressSubComponent.nameWarning(false);

					bodyPanel.add(shipToAddressSubComponent);

					if (checkoutModel.getShippings().size() > 0)
					{
						shippingSubComponent = new CheckoutShippingSubComponent(storeController, shippingSubComponentCallback);
						CommonUtils.addSubTitle(handle, bodyPanel, constants.CheckoutComponentShippingTitle());
						bodyPanel.add(shippingSubComponent);
						shippingSubComponent.setCheckoutCompleted(checkoutCompleted);
						shippingSubComponent.build();
					}

					InlineLabel billToTitle = new InlineLabel(constants.CheckoutComponentBillToAddress());
					billToTitle.setStyleName(handle + "SubTitle");
					bodyPanel.add(billToTitle);
					sameAsShipTo = new CheckBox();
					sameAsShipTo.addClickHandler(new ClickHandler() {
						public void onClick(ClickEvent event) {
							if (sameAsShipTo.getValue())
							{
								billToAddressSubComponent.setVisible(false);
								shipToAddressSubComponent.nameWarning(true);
								sameAsShipToLabel.setHTML(storeController.getStoreConstants().CheckoutComponentSameAsShipping() + "<br/><br/>");
							}
							else
							{
								billToAddressSubComponent.setVisible(true);
								shipToAddressSubComponent.nameWarning(false);
								billToAddressSubComponent.nameWarning(true);
								sameAsShipToLabel.setHTML(storeController.getStoreConstants().CheckoutComponentSameAsShipping() + "<br/>");
							}
						}
					});
					sameAsShipTo.setVisible(!checkoutCompleted);
					bodyPanel.add(new InlineHTML("&nbsp;&nbsp;"));
					bodyPanel.add(sameAsShipTo);
					sameAsShipToLabel = new InlineHTML(storeController.getStoreConstants().CheckoutComponentSameAsShipping() + "<br/>");
					sameAsShipToLabel.setVisible(!checkoutCompleted || sameAsShipTo.getValue());
					bodyPanel.add(sameAsShipToLabel);

					billToAddressSubComponent =
						new CheckoutAddressSubComponent(
								storeController, 
								addressSubComponentCallback);

					billToAddressSubComponent.setAddressModel(addressModel);

					billToAddressSubComponent.setCheckoutCompleted(checkoutCompleted);
					billToAddressSubComponent.build();
					billToAddressSubComponent.nameWarning(true);

					bodyPanel.add(billToAddressSubComponent);

					if (checkoutModel.getPayments().size() > 0)
					{
						paymentSubComponent = new CheckoutPaymentSubComponent(storeController,  null);
						paymentSubComponent.setCheckoutCompleted(checkoutCompleted);
						CommonUtils.addSubTitle(handle, bodyPanel, constants.CheckoutComponentPaymentTitle());
						bodyPanel.add(paymentSubComponent);
						paymentSubComponent.build();
					}

					couponTitle = new InlineLabel(constants.CheckoutComponentDiscountCoupon());
					couponTitle.setStyleName(handle + "SubTitle");
					couponTitle.setVisible(!checkoutCompleted);
					bodyPanel.add(couponTitle);

					couponTextBox = new TextBox();
					couponTextBox.addKeyUpHandler(new KeyUpHandler() {
						public void onKeyUp(KeyUpEvent event) {
							if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
								couponText = couponTextBox.getText();
								couponTextBox.setText("");
								submit("");
							}
						}
					});

					couponGrid = new Grid(1,3);
					couponGrid.getCellFormatter().setHorizontalAlignment(0, 1, HasHorizontalAlignment.ALIGN_RIGHT);
					couponGrid.setWidth("100%");
					couponGrid.setStyleName(handle + "SubPanel");
					couponGrid.getColumnFormatter().setStyleName(0, handle + "SubPanelCol0");
					couponGrid.setText(0, 0, constants.CheckoutComponentDiscountCouponCode());
					couponGrid.getCellFormatter().setWidth(0, 0, "100%");
					couponGrid.setWidget(0, 1, couponTextBox);

					Button couponButton = new Button(constants.CheckoutComponentAddCoupon(),
							new ClickHandler() {
								public void onClick(ClickEvent event) {
									couponText = couponTextBox.getText();
									couponTextBox.setText("");
									submit("");
								}}
					);
					couponGrid.setWidget(0, 2, couponButton);
					couponGrid.setVisible(!checkoutCompleted);

					bodyPanel.add(couponGrid);

					bodyPanel.add(new InlineHTML("<br/>"));
					commentTitle = new InlineLabel(constants.CheckoutComponentCommentTitle());
					commentTitle.setStyleName(handle + "SubTitle");
					commentTitle.setVisible(!checkoutCompleted);
					bodyPanel.add(commentTitle);

					commentTextArea = new TextArea();
					commentTextArea.setWidth("100%");
					commentTextArea.setCharacterWidth(200);
					commentTextArea.setVisibleLines(5);
					commentTextArea.setVisible(!checkoutCompleted);
					bodyPanel.add(commentTextArea);
					commentText = new InlineLabel();
					commentText.setText(commentTextArea.getValue());
					commentText.setVisible(checkoutCompleted);
					bodyPanel.add(commentText);

					totalsPanel = new VerticalPanel();
					totalsPanel.setWidth("100%");
					bodyPanel.add(totalsPanel);

					paymentButton = new Button(constants.CheckoutComponentPlaceYourOrder(),
							new ClickHandler() {
								public void onClick(ClickEvent event) {
									submit(CheckoutComponent.ProcessPayment);
								}}
					);
					paymentButton.setVisible(!checkoutCompleted);
					bodyPanel.add(paymentButton);

				}
				else
				{
					checkoutInstructionsLabel.setVisible(!checkoutCompleted);
					checkoutThankyouLabel.setVisible(checkoutCompleted);
					requiredTitle.setVisible(!checkoutCompleted);
					shipToAddressSubComponent.setCheckoutCompleted(checkoutCompleted);
					shipToAddressSubComponent.build();
					sameAsShipTo.setVisible(!checkoutCompleted);
					sameAsShipToLabel.setVisible(!checkoutCompleted || sameAsShipTo.getValue());
					billToAddressSubComponent.setCheckoutCompleted(checkoutCompleted);
					billToAddressSubComponent.build();

					if (sameAsShipTo.getValue())
					{
						billToAddressSubComponent.setVisible(false);
						shipToAddressSubComponent.nameWarning(true);
						sameAsShipToLabel.setHTML(storeController.getStoreConstants().CheckoutComponentSameAsShipping() + "<br/><br/>");
					}
					else
					{
						billToAddressSubComponent.setVisible(true);
						billToAddressSubComponent.nameWarning(true);
						shipToAddressSubComponent.nameWarning(false);
						sameAsShipToLabel.setHTML(storeController.getStoreConstants().CheckoutComponentSameAsShipping() + "<br/>");
					}


					if (shippingSubComponent != null)
					{
						shippingSubComponent.setCheckoutCompleted(checkoutCompleted);
						shippingSubComponent.build();
					}
					if (paymentSubComponent != null)
					{
						paymentSubComponent.setCheckoutCompleted(checkoutCompleted);
						paymentSubComponent.build();
					}
					couponTitle.setVisible(!checkoutCompleted);
					couponGrid.setVisible(!checkoutCompleted);
					commentTitle.setVisible(!checkoutCompleted);
					commentTextArea.setVisible(!checkoutCompleted);
					commentText.setText(commentTextArea.getValue());
					commentText.setVisible(checkoutCompleted);
					paymentButton.setVisible(!checkoutCompleted);
				}

				serverError(checkoutModel);

				panel.setWidget(1, 0, bodyPanel);

				totalsPanel.clear();

				CartModel cartModel = StoreComponentController.cartComponent.getCartModel();

				List<DetailedProductModel> productModels = cartModel.getProducts();
				int sz = productModels.size();
				Grid cartTable = new Grid(sz,3);
				cartTable.setWidth("100%");
				CellFormatter cf = cartTable.getCellFormatter();
				for (int i= 0; i < sz; i++)
				{
					cartTable.setHTML(i,0,"&nbsp");
					cf.setWidth(i, 0, "100%");

					DetailedProductModel productModel = productModels.get(i);

					//Product
					List<AttributeValueModel> values = productModel.getCartOptions();
					int osz = values.size();
					Grid productGrid = new Grid(1+osz,1);
					int quantity = productModel.getCartQuantity();
					productGrid.getCellFormatter().setWordWrap(0, 0, false);
					productGrid.setText(0, 0, quantity + " X " + productModel.getName());
					for (int j= 0; j < osz; j++)
					{
						AttributeValueModel valueModel = values.get(j);
						String txt = "- " + valueModel.getAttributeName() + ": " + valueModel.getName();
						productGrid.getCellFormatter().setWordWrap(j+1, 0, false);
						productGrid.setText(j+1, 0, txt);
					}

					cartTable.setWidget(i, 1, productGrid);

					//Price
					double totalPrice = productModel.getCartPrice() * quantity;
					cartTable.setHTML(i,2,CommonUtils.priceDisplay (StoreComponentController.currenciesComponent.getCurrencyModel(), totalPrice));
					cf.setHorizontalAlignment(i, 2, HasHorizontalAlignment.ALIGN_RIGHT);
				}
				totalsPanel.add(cartTable);

				if (checkoutCompleted)
				{
					StoreComponentController.cartComponent.getCartModel().clearProducts();
				}

				List<OrderTotalModel> totals = checkoutModel.getTotals();
				int numTotals = totals.size();
				Grid detailsTotals = new Grid(numTotals,2);
				detailsTotals.setWidth("100%");
				cf = detailsTotals.getCellFormatter();
				for (int i = 0; i< numTotals; i++)
				{
					OrderTotalModel total = totals.get(i);
					String title = total.getTitle();
					String error = total.getError();
					if (total.getTotalClass().equals("ot_shipping")) title = shippingSubComponent.getSelectedTitle() + ":";
					if (error != null && error.length() > 0) title = "<span class=\"WarningLabel\">" + title + "</span>";
					detailsTotals.setWidget(i,0,new InlineHTML(title));
					cf.setHorizontalAlignment(i, 0, HasHorizontalAlignment.ALIGN_RIGHT);
					String totalLabel = CommonUtils.priceDisplay(StoreComponentController.currenciesComponent.getCurrencyModel(), total.getValue());
					detailsTotals.setHTML(i,1,totalLabel);
					cf.setHorizontalAlignment(i, 1, HasHorizontalAlignment.ALIGN_RIGHT);
					cf.setWidth(i,0, "100%");
				}
				totalsPanel.add(detailsTotals);
				totalsPanel.setCellWidth(detailsTotals, "100%");
			}
		}
		onBuilt();
	}

	/**
	 * 
	 * @return
	 */
	public List<CountryModel> getCountryModels() {
		return countryModels;
	}

	protected SubComponentCallback addressSubComponentCallback = 
		new SubComponentCallback() {

		public void onNavigate(CommonSubComponent subComponent) {
		}

		public void onSuccess(CommonSubComponent subComponent) {
			checkoutModel.setSelectedShippingModule("");
			checkoutModel.setSelectedShippingService("");
			submit("");
		}

		public void onError(CommonSubComponent subComponent, Throwable exception) {}
	};

	protected SubComponentCallback shippingSubComponentCallback = 
		new SubComponentCallback() {

		public void onNavigate(CommonSubComponent subComponent) {
		}

		public void onSuccess(CommonSubComponent subComponent) {
			submit("");
		}

		public void onError(CommonSubComponent subComponent, Throwable exception) {}
	};

	protected void submit(String action)
	{
		this.action = action;
		checkoutModel.setActionCompleted("");
		if (action != null && action.equals(ProcessPayment)) {
			if (validate(action))
			{
				if (paymentSubComponent.isAjaxCheckout()) {
					Window.scrollTo(0, 0);
					build(true);
				}
				else {
					buildRequestParameters();
					paymentSubComponent.build();
					paymentSubComponent.submit(getRequestParameters());
				}
			}
		}
		else {
			build(true);
		}
	}

	protected boolean validate(String action)
	{
		String warningText = "";

		warningText += shipToAddressSubComponent.validate(action);
		warningText += billToAddressSubComponent.validate(action);
		warningText += shippingSubComponent.validate(action);
		warningText += paymentSubComponent.validate(action);

		if (warningText.length() > 0) {
			warningLabel.setHTML("<br/><br/><span class=\"WarningLabel\">" + warningText + "<span><br/>");
			Window.scrollTo(0, 0);
			return false;
		}
		else {
			warningLabel.setHTML("<br/>");
		}	
		return true;
	}

	protected boolean serverError(CheckoutModel checkoutModel)
	{
		GWTCommerceConstants constants = storeController.getStoreConstants();
		GWTCommerceCommonConstants commonConstants = controller.getCommonConstants();
		String warningText = "";
		switch (checkoutModel.getErrorCode())
		{
		case 0: warningText = ""; break;
		case -1: warningText = checkoutModel.getErrorMessage(); 
		try {
			warningText = storeController.getStoreModuleConstants().getString(warningText);
		}
		catch (Exception e){}
		warningText += " -- " + checkoutModel.getPaymentErrorMessage();
		break;
		case -5: warningText = constants.CheckoutComponentEmptyCart(); break;
		case -10: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentShipTo() + " " + constants.AccountComponentPostCode() + " ", accountMetaModel.getEntryPostCodeLengthMin()); break;
		case -20: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentShipTo() + " " + constants.AccountComponentState() + " ", accountMetaModel.getEntryStateLengthMin()); break;
		case -30: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentBillTo() + " " + constants.AccountComponentPostCode() + " ", accountMetaModel.getEntryPostCodeLengthMin()); break;
		case -40: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentBillTo() + " " + constants.AccountComponentState() + " ", accountMetaModel.getEntryStateLengthMin()); break;
		case -50: warningText = CommonUtils.lengthWarning(commonConstants, constants.AccountComponentEmailAddress() + " ", accountMetaModel.getEntryEmailAddressLengthMin()); break;
		case -60: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentShipTo() + " " + constants.AccountComponentStreetAddress() + " ", accountMetaModel.getEntryStreetAddressLengthMin()); break;
		case -70: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentShipTo() + " " + constants.AccountComponentCity() + " ", accountMetaModel.getEntryCityLengthMin()); break;
		case -80: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentBillTo() + " " + constants.AccountComponentStreetAddress() + " ", accountMetaModel.getEntryStreetAddressLengthMin()); break;
		case -90: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentBillTo() + " " + constants.AccountComponentCity() + " ", accountMetaModel.getEntryCityLengthMin()); break;
		case -100: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentShipTo() + " " + constants.CheckoutComponentName() + " ", checkoutMetaModel.getEntryCreditCardOwnerLengthMin()); break;
		case -110: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentBillTo() + " " + constants.CheckoutComponentName() + " ", checkoutMetaModel.getEntryCreditCardOwnerLengthMin()); break;
		case -120: warningText = CommonUtils.lengthWarning(commonConstants, constants.CheckoutComponentCreditCardNumber() + " ", checkoutMetaModel.getEntryCreditCardNumberLengthMin()); break;
		case -200: warningText = "Out of Stock"; break; //TBD - full message including which product(s) is out of stock.

		case -1000: warningText = checkoutModel.getErrorMessage();
		default: warningText = ""; break;
		}
		if (warningText.length() > 0) {
			warningLabel.setHTML("<br/><br/><span class=\"WarningLabel\">" + warningText + "<span><br/>");
			return false;
		}
		else {
			warningLabel.setHTML("<br/>");
		}
		return true;
	}

	public CheckoutMetaModel getCheckoutMetaModel() {
		return checkoutMetaModel;
	}

	public CheckoutModel getCheckoutModel() {
		return checkoutModel;
	}
}
